static gboolean
checkout_file_from_input_at (OstreeRepo *self,
- OstreeRepoCheckoutMode mode,
+ OstreeRepoCheckoutOptions *options,
GFileInfo *file_info,
GVariant *xattrs,
GInputStream *input,
goto out;
}
- if (mode != OSTREE_REPO_CHECKOUT_MODE_USER)
+ if (options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{
if (G_UNLIKELY (fchownat (destination_dfd, destination_name,
g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Don't make setuid files on checkout when we're doing --user */
- if (mode == OSTREE_REPO_CHECKOUT_MODE_USER)
+ if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
file_mode &= ~(S_ISUID|S_ISGID);
do
temp_out = g_unix_output_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
- if (!write_regular_file_content (self, mode, temp_out, file_info, xattrs, input,
+ if (!write_regular_file_content (self, options->mode, temp_out, file_info, xattrs, input,
cancellable, error))
goto out;
}
*/
static gboolean
checkout_file_unioning_from_input_at (OstreeRepo *repo,
- OstreeRepoCheckoutMode mode,
+ OstreeRepoCheckoutOptions *options,
GFileInfo *file_info,
GVariant *xattrs,
GInputStream *input,
file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Don't make setuid files on checkout when we're doing --user */
- if (mode == OSTREE_REPO_CHECKOUT_MODE_USER)
+ if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
file_mode &= ~(S_ISUID|S_ISGID);
if (!gs_file_open_in_tmpdir_at (destination_dfd, file_mode,
cancellable, error))
goto out;
- if (!write_regular_file_content (repo, mode, temp_out, file_info, xattrs, input,
+ if (!write_regular_file_content (repo, options->mode, temp_out, file_info, xattrs, input,
cancellable, error))
goto out;
}
static gboolean
checkout_file_hardlink (OstreeRepo *self,
- OstreeRepoCheckoutMode mode,
- OstreeRepoCheckoutOverwriteMode overwrite_mode,
+ OstreeRepoCheckoutOptions *options,
const char *loose_path,
int destination_dfd,
const char *destination_name,
{
ret_was_supported = FALSE;
}
- else if (errno == EEXIST && overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
+ else if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
{
/* Idiocy, from man rename(2)
*
static gboolean
checkout_one_file_at (OstreeRepo *repo,
+ OstreeRepoCheckoutOptions *options,
GFile *source,
GFileInfo *source_info,
int destination_dfd,
const char *destination_name,
- OstreeRepoCheckoutMode mode,
- OstreeRepoCheckoutOverwriteMode overwrite_mode,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
const char *checksum;
gboolean is_symlink;
+ gboolean can_cache;
gboolean did_hardlink = FALSE;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
gs_unref_object GInputStream *input = NULL;
while (current_repo)
{
gboolean is_bare = ((current_repo->mode == OSTREE_REPO_MODE_BARE
- && mode == OSTREE_REPO_CHECKOUT_MODE_NONE) ||
+ && options->mode == OSTREE_REPO_CHECKOUT_MODE_NONE) ||
(current_repo->mode == OSTREE_REPO_MODE_BARE_USER
- && mode == OSTREE_REPO_CHECKOUT_MODE_USER));
+ && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER));
+ gboolean current_can_cache = (options->enable_uncompressed_cache
+ && current_repo->enable_uncompressed_cache);
gboolean is_archive_z2_with_cache = (current_repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2
- && mode == OSTREE_REPO_CHECKOUT_MODE_USER
- && current_repo->enable_uncompressed_cache);
+ && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER
+ && current_can_cache);
/* But only under these conditions */
if (is_bare || is_archive_z2_with_cache)
the cache, which is in "bare" form */
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE);
if (!checkout_file_hardlink (current_repo,
- mode, overwrite_mode, loose_path_buf,
+ options,
+ loose_path_buf,
destination_dfd, destination_name,
TRUE, &did_hardlink,
cancellable, error))
}
}
+ can_cache = (options->enable_uncompressed_cache
+ && repo->enable_uncompressed_cache);
+
/* Ok, if we're archive-z2 and we didn't find an object, uncompress
* it now, stick it in the cache, and then hardlink to that.
*/
- if (!is_symlink
+ if (can_cache
+ && !is_symlink
&& !did_hardlink
&& repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2
- && mode == OSTREE_REPO_CHECKOUT_MODE_USER
- && repo->enable_uncompressed_cache)
+ && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
{
if (!ostree_repo_load_file (repo, checksum, &input, NULL, NULL,
cancellable, error))
}
g_mutex_unlock (&repo->cache_lock);
- if (!checkout_file_hardlink (repo, mode, overwrite_mode, loose_path_buf,
+ if (!checkout_file_hardlink (repo, options, loose_path_buf,
destination_dfd, destination_name,
FALSE, &did_hardlink,
cancellable, error))
cancellable, error))
goto out;
- if (overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
+ if (options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
{
- if (!checkout_file_unioning_from_input_at (repo, mode, source_info, xattrs, input,
+ if (!checkout_file_unioning_from_input_at (repo, options, source_info, xattrs, input,
destination_dfd,
destination_name,
cancellable, error))
}
else
{
- if (!checkout_file_from_input_at (repo, mode, source_info, xattrs, input,
+ if (!checkout_file_from_input_at (repo, options, source_info, xattrs, input,
destination_dfd,
destination_name,
cancellable, error))
*/
static gboolean
checkout_tree_at (OstreeRepo *self,
- OstreeRepoCheckoutMode mode,
- OstreeRepoCheckoutOverwriteMode overwrite_mode,
+ OstreeRepoCheckoutOptions *options,
int destination_parent_fd,
const char *destination_name,
OstreeRepoFile *source,
while (G_UNLIKELY (res == -1 && errno == EINTR));
if (res == -1)
{
- if (errno == EEXIST && overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
+ if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
did_exist = TRUE;
else
{
goto out;
/* Set the xattrs now, so any derived labeling works */
- if (!did_exist && mode != OSTREE_REPO_CHECKOUT_MODE_USER)
+ if (!did_exist && options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{
if (!ostree_repo_file_get_xattrs (source, &xattrs, NULL, error))
goto out;
if (g_file_info_get_file_type (source_info) != G_FILE_TYPE_DIRECTORY)
{
- ret = checkout_one_file_at (self, (GFile *) source,
+ ret = checkout_one_file_at (self, options,
+ (GFile *) source,
source_info,
destination_dfd,
g_file_info_get_name (source_info),
- mode, TRUE,
cancellable, error);
goto out;
}
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
{
- if (!checkout_tree_at (self, mode, overwrite_mode,
+ if (!checkout_tree_at (self, options,
destination_dfd, name,
(OstreeRepoFile*)src_child, file_info,
cancellable, error))
}
else
{
- if (!checkout_one_file_at (self, src_child, file_info,
+ if (!checkout_one_file_at (self, options,
+ src_child, file_info,
destination_dfd, name,
- mode, overwrite_mode,
cancellable, error))
goto out;
}
}
}
- if (!did_exist && mode != OSTREE_REPO_CHECKOUT_MODE_USER)
+ if (!did_exist && options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{
do
res = fchown (destination_dfd,
GCancellable *cancellable,
GError **error)
{
- return checkout_tree_at (self, mode, overwrite_mode,
- AT_FDCWD,
- gs_file_get_path_cached (destination),
+ OstreeRepoCheckoutOptions options = { 0, };
+
+ options.mode = mode;
+ options.overwrite_mode = overwrite_mode;
+ /* Backwards compatibility */
+ options.enable_uncompressed_cache = TRUE;
+
+ return checkout_tree_at (self, &options,
+ AT_FDCWD, gs_file_get_path_cached (destination),
source, source_info,
cancellable, error);
}
+/**
+ * ostree_repo_checkout_tree_at:
+ * @self: Repo
+ * @options: (allow-none): Options
+ * @destination_dfd: Directory FD for destination
+ * @destination_path: Directory for destination
+ * @commit: Checksum for commit
+ * @subpath: (allow-none): Subdirectory path
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Similar to ostree_repo_checkout_tree(), but uses directory-relative
+ * paths for the destination, uses a new `OstreeRepoCheckoutOptions`,
+ * and takes a commit checksum and optional subpath pair, rather than
+ * requiring use of `GFile` APIs for the caller.
+ *
+ * Note in addition that unlike ostree_repo_checkout_tree(), the
+ * default is not to use the repository-internal uncompressed objects
+ * cache.
+ */
+gboolean
+ostree_repo_checkout_tree_at (OstreeRepo *self,
+ OstreeRepoCheckoutOptions *options,
+ int destination_dfd,
+ const char *destination_path,
+ const char *commit,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_unref_object GFile* commit_root = NULL;
+ gs_unref_object GFile* target_dir = NULL;
+ gs_unref_object GFileInfo* target_info = NULL;
+ OstreeRepoCheckoutOptions default_options = { 0, };
+
+ if (!options)
+ {
+ default_options.subpath = NULL;
+ options = &default_options;
+ }
+
+ commit_root = (GFile*) _ostree_repo_file_new_for_commit (self, commit, error);
+ if (!commit_root)
+ goto out;
+
+ if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)commit_root, error))
+ goto out;
+
+ if (options->subpath && strcmp (options->subpath, "/") != 0)
+ target_dir = g_file_get_child (commit_root, options->subpath);
+ else
+ target_dir = g_object_ref (commit_root);
+ target_info = g_file_query_info (target_dir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ if (!target_info)
+ goto out;
+
+ if (!checkout_tree_at (self, options,
+ destination_dfd,
+ destination_path,
+ (OstreeRepoFile*)target_dir, target_info,
+ cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
/**
* ostree_repo_checkout_gc:
* @self: Repo
static gboolean opt_user_mode;
static gboolean opt_allow_noent;
+static gboolean opt_disable_cache;
static char *opt_subpath;
static gboolean opt_union;
static gboolean opt_from_stdin;
static GOptionEntry options[] = {
{ "user-mode", 'U', 0, G_OPTION_ARG_NONE, &opt_user_mode, "Do not change file ownership or initialize extended attributes", NULL },
+ { "disable-cache", 0, 0, G_OPTION_ARG_NONE, &opt_disable_cache, "Do not update or use the internal repository uncompressed object cache", NULL },
{ "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" },
{ "union", 0, 0, G_OPTION_ARG_NONE, &opt_union, "Keep existing directories, overwrite existing files", NULL },
{ "allow-noent", 0, 0, G_OPTION_ARG_NONE, &opt_allow_noent, "Do nothing if specified path does not exist", NULL },
process_one_checkout (OstreeRepo *repo,
const char *resolved_commit,
const char *subpath,
- GFile *target,
+ const char *destination,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
- GError *tmp_error = NULL;
- gs_unref_object GFile *root = NULL;
- gs_unref_object GFile *subtree = NULL;
- gs_unref_object GFileInfo *file_info = NULL;
- if (!ostree_repo_read_commit (repo, resolved_commit, &root, NULL, cancellable, error))
- goto out;
-
- if (subpath)
- subtree = g_file_resolve_relative_path (root, subpath);
+ /* This strange code structure is to preserve testing
+ * coverage of both `ostree_repo_checkout_tree` and
+ * `ostree_repo_checkout_tree_at` until such time as we have a more
+ * convenient infrastructure for testing C APIs with data.
+ */
+ if (opt_disable_cache)
+ {
+ OstreeRepoCheckoutOptions options = { 0, };
+
+ if (opt_user_mode)
+ options.mode = OSTREE_REPO_CHECKOUT_MODE_USER;
+ if (opt_union)
+ options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
+ if (subpath)
+ options.subpath = subpath;
+
+
+ if (!ostree_repo_checkout_tree_at (repo, &options,
+ AT_FDCWD, destination,
+ resolved_commit,
+ cancellable, error))
+ goto out;
+ }
else
- subtree = g_object_ref (root);
-
- file_info = g_file_query_info (subtree, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, &tmp_error);
- if (!file_info)
{
- if (opt_allow_noent
- && g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&tmp_error);
- ret = TRUE;
- }
+ GError *tmp_error = NULL;
+ gs_unref_object GFile *root = NULL;
+ gs_unref_object GFile *subtree = NULL;
+ gs_unref_object GFileInfo *file_info = NULL;
+ gs_unref_object GFile *destination_file = g_file_new_for_path (destination);
+
+ if (!ostree_repo_read_commit (repo, resolved_commit, &root, NULL, cancellable, error))
+ goto out;
+
+ if (subpath)
+ subtree = g_file_resolve_relative_path (root, subpath);
else
+ subtree = g_object_ref (root);
+
+ file_info = g_file_query_info (subtree, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, &tmp_error);
+ if (!file_info)
{
- g_propagate_error (error, tmp_error);
+ if (opt_allow_noent
+ && g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&tmp_error);
+ ret = TRUE;
+ }
+ else
+ {
+ g_propagate_error (error, tmp_error);
+ }
+ goto out;
}
- goto out;
- }
- if (!ostree_repo_checkout_tree (repo, opt_user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
- opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0,
- target, OSTREE_REPO_FILE (subtree), file_info, cancellable, error))
- goto out;
+ if (!ostree_repo_checkout_tree (repo, opt_user_mode ? OSTREE_REPO_CHECKOUT_MODE_USER : 0,
+ opt_union ? OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES : 0,
+ destination_file,
+ OSTREE_REPO_FILE (subtree), file_info,
+ cancellable, error))
+ goto out;
+ }
ret = TRUE;
out:
static gboolean
process_many_checkouts (OstreeRepo *repo,
- GFile *target,
+ const char *target,
GCancellable *cancellable,
GError **error)
{
const char *commit;
const char *destination;
gs_free char *resolved_commit = NULL;
- gs_unref_object GFile *checkout_target = NULL;
context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree");
if (opt_from_stdin || opt_from_file)
{
destination = argv[1];
- checkout_target = g_file_new_for_path (destination);
- if (!process_many_checkouts (repo, checkout_target, cancellable, error))
+ if (!process_many_checkouts (repo, destination, cancellable, error))
goto out;
}
else
if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error))
goto out;
- checkout_target = g_file_new_for_path (destination);
-
if (!process_one_checkout (repo, resolved_commit, opt_subpath,
- checkout_target,
+ destination,
cancellable, error))
goto out;
}